#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import sys
import json

from common.common import execute_cmd, exter_attack, check_command_injection
from common.const import SysData
from common.cleaner import clear
from common.parse_parafile import ParamFileUtil
from exchange import log
from exchange.commons.command import Commands
from exchange.commons.common import output_tmp_info, check_del_dir_or_file, output_action_result
from exchange.commons.const import ParamConstant, JobData, BodyErr, ExchangeCode, SYSTEM_MAILBOX_LIST
from exchange.commons.common_models import ListAppV2Response
from exchange.commons.powershell import execute_with_utf8



class ExchangeResourceCommon:
    """
    m_check_output = {'code': ExchangeCode.FAILED.value, 'bodyErr': 0, 'message': ''}
    m_func_type = "" 要调用的函数名，调用检查时必会赋值
    m_username = "" 用户名，从系统输入的appEnv_auth_authKey_{JobData.PID}字段获取
    m_password = "" 密码，从系统输入的appEnv_auth_authPwd_{JobData.PID}字段获取
    """
    m_check_output = {'code': ExchangeCode.FAILED.value, 'bodyErr': 0, 'message': ''}
    m_func_type = ""
    m_username = ""
    m_password = ""
    param = {}

    # 检查stmp文件夹路径是否存在
    @staticmethod
    def check_result_path():
        """
        check result path exit or not
        """
        if os.path.exists(ParamConstant.RESULT_PATH):
            return True
        else:
            log.error("The result file path does not exist!")
            return False

    # 将结果写入stmp文件
    @staticmethod
    def write_result_to_file(in_str):
        log.info("Enter write_result_to_file")
        file_name = "{}{}".format("result", JobData.PID)
        file_path = os.path.join(ParamConstant.RESULT_PATH, file_name)
        output_tmp_info(file_path, in_str)

    @staticmethod
    def del_param_file():
        # 删除UBC下发的参数
        # 资源接入暂时没有用到，直接删除即可
        tmp_file_name = "{}{}".format("param", JobData.PID)
        tmp_file_path = os.path.join(ParamConstant.PARAM_FILE_PATH, tmp_file_name)
        check_del_dir_or_file(tmp_file_path)

    def init_path_params(self, args_list):
        # func pid key pwd
        self.m_func_type = args_list[0]
        JobData.PID = args_list[1]
        stdin_dict = json.loads(SysData.SYS_STDIN)
        self.m_username = stdin_dict[f'appEnv_auth_authKey_{JobData.PID}']
        self.m_password = stdin_dict[f"appEnv_auth_authPwd_{JobData.PID}"]
        self.param = ParamFileUtil.parse_param_file(JobData.PID)
        if not self.m_username or not self.m_password:
            log.error("get user info failed")
        if check_command_injection(self.m_username):
            log.error(f"The param: %s has special characters.", self.m_username)
            return False
        return True

    # 资源扫描
    @exter_attack
    def list_application_resource(self):
        log.info("Enter list_application_resource")
        condition = self.param.get("condition")
        response = ListAppV2Response()
        if condition and condition.get("conditions"):
            # 邮箱资源扫描
            query_finished = self.list_mailboxes_by_database(condition, response)
            # 扫描出错直接返回
            if not query_finished:
                return False
            log.info(f"mailbox resource scan, size: {response.total}")
        else:
            # 数据库资源扫描
            # "\" 转为 "\\"
            username = self.m_username.replace("\\", "\\\\")
            password = self.m_password.replace("$", "`$")
            # 拼接cmd命令，执行powershell脚本
            ps1_path = ParamConstant.PS1_SCRIPTS_PATH + "GetAllMountedMailboxDatabases.ps1"
            cmds = f"{ParamConstant.PS_LOC} {ps1_path} \\\"{username}\\\" \\\"{password}\\\""
            ret, std_out, std_err = execute_cmd(cmds)
            result = '\n'.join(std_out.splitlines()[-1:])
            result = json.loads(result)
            return_code = result.get("ReturnCode", "")
            if return_code != "0":
                log.error("ReturnCode: %s, ErrorMessage: %s", return_code, result.get("Output", ""))
                return False
            response.total = 100
            resource_list = []
            if result.get("Output", []):
                for temp in result.get("Output"):
                    resource = dict()
                    resource["type"] = "Database"
                    resource["subType"] = "Exchange-database"
                    resource["version"] = temp["version"]
                    resource["uuid"] = temp["db_uuid"]
                    resource["extendInfo"] = temp
                    resource_list.append(resource)
            response.resource_list = resource_list
            log.info("List over This is response.dict: %s", json.dumps(response.dict(by_alias=True)))
        # ensure_ascii设置为False，兼容其他语言字符
        self.write_result_to_file(json.dumps(response.dict(by_alias=True), ensure_ascii=False))
        return True

    def list_mailboxes_by_database(self, condition, response):
        mailbox_list = []
        database = condition["conditions"]["database"]
        log.info(f"Enter list_mailboxes_by_database from database: {database}")
        page_size = int(condition.get("pageSize", 0))
        need_skip_num = page_size * int(condition.get("pageNo", 0))
        script = Commands.GET_MAILBOX_LIST_BY_DATABASE.format(database_name=database, first=page_size,
                                                              skip=need_skip_num)
        result = execute_with_utf8(self.m_username, self.m_password, script)
        return_code = result.get("ReturnCode", "")
        if return_code != "0":
            log.error("Get Mailbox ReturnCode: %s, ErrorMessage: %s", return_code, result.get("Output", ""))
            return False
        if result.get("Output", []):
            result_obj = json.loads(result.get("Output"))
            # 返回结果只有一条时，创建一个list
            if not isinstance(result_obj, list):
                result_obj = [result_obj]
            response.total = len(result_obj)
            for mailbox in result_obj:
                resource = dict()
                resource["type"] = "Database"
                resource["subType"] = "Exchange-mailbox"
                resource["uuid"] = mailbox["ExchangeGuid"]
                mailbox_name = mailbox["Name"]
                # 过滤掉系统内置邮箱
                if any([mailbox_name.startswith(mail) for mail in SYSTEM_MAILBOX_LIST]):
                    log.info(f"{mailbox_name} is system mailbox, skip!")
                    continue
                resource["name"] = mailbox["DisplayName"]
                mailbox["PrimarySmtpAddress"] = mailbox["PrimarySmtpAddress"]["Local"] + "@" \
                    + mailbox["PrimarySmtpAddress"]["Domain"]
                mailbox["RecipientType"] = mailbox["RecipientType"]["Value"]
                mailbox["DatabaseName"] = database
                resource["extendInfo"] = mailbox
                mailbox_list.append(resource)
        response.resource_list = mailbox_list
        log.info(f"Get mailbox size: {len(mailbox_list)}, skip: {need_skip_num}")
        return True

    # 测试连通性
    @exter_attack
    def check_exchange_connection(self):
        log.info("Enter check_exchange_connection")
        username = self.m_username.replace("\\", "\\\\")
        password = self.m_password.replace("$", "`$")
        ps1_path = ParamConstant.PS1_SCRIPTS_PATH + "CheckAppConnection.ps1"
        cmds = f"{ParamConstant.PS_LOC} {ps1_path} \"{username}\" \"{password}\""
        ret, std_out, std_err = execute_cmd(cmds)
        # 移除冗余字符
        result = '\n'.join(std_out.splitlines()[-1:])
        result = json.loads(result)
        if int(result["ReturnCode"], base=16) == ExchangeCode.SUCCESS.value:
            output_action_result(JobData.PID, ExchangeCode.SUCCESS.value, ExchangeCode.SUCCESS.value,
                                f"Exchange server is OK")
            return True
        # 部分数据库服务未启动
        if int(result["ReturnCode"], base=16) == BodyErr.ERR_DB_SERVICES.value:
            body_err_params = [result["Output"]]
            output_action_result(JobData.PID, ExchangeCode.FAILED.value, BodyErr.ERR_DB_SERVICES.value,
                                f"Service is failed", body_err_params)
            log.error("err: %s", std_err)
            return False
        output_action_result(JobData.PID, ExchangeCode.FAILED.value, int(result["ReturnCode"], base=16),
                            result["Output"])
        log.error("err: %s", std_err)
        return False

    # 查询主机所在dag组
    @exter_attack
    def get_dag_group_servers(self):
        log.info("Enter get_dag_group_servers")
        # "\" 转为 "\\"
        username = self.m_username.replace("\\", "\\\\")
        password = self.m_password.replace("$", "`$")
        ps1_path = ParamConstant.PS1_SCRIPTS_PATH + "GetDAGGroupServers.ps1"
        # 拼接cmd命令，执行powershell脚本
        cmds = f"{ParamConstant.PS_LOC} {ps1_path} \"{username}\" \"{password}\""
        ret, std_out, std_err = execute_cmd(cmds)
        result = '\n'.join(std_out.splitlines()[-1:])
        log.info("Write the result into file: %s", result)
        self.write_result_to_file(result)
        log.info("Leave get_dag_group_servers")
        return True

    def clear_pwd(self):
        clear(self.m_password)


def convert_whitespace(string: str) -> str:
    """
    string中存在空格，字符串需要加上引号
    :param string: 字符串
    :return: 加上引号的字符串
    """
    if " " in string:
        return "\"" + string + "\""
    return string


def resource_main(args_list):
    resource_common_instance = ExchangeResourceCommon()
    if not resource_common_instance.init_path_params(args_list):
        log.error("Contains invalid characters while registering. Please check.")
        output_action_result(JobData.PID, ExchangeCode.FAILED.value, BodyErr.LOGIN_FAILED.value,
                            f"Invalid characters")
        return False
    resource_common_instance.del_param_file()
    cmd_dict = {
        "CheckApplication": [resource_common_instance.check_exchange_connection],
        "ListApplicationResourceV2": [resource_common_instance.list_application_resource],
        "QueryHostCluster": [resource_common_instance.get_dag_group_servers]
    }
    func_array = cmd_dict.get(resource_common_instance.m_func_type)
    if not func_array:
        output_action_result(JobData.PID, ExchangeCode.FAILED.value, BodyErr.ERROR_INTERNAL.value,
                            f"Cmd Param error.")
        return False
    try:
        ret = func_array[0]()
    except Exception as e:
        log.error("Exception: %s.", str(e))
        output_action_result(JobData.PID, ExchangeCode.FAILED.value, BodyErr.ERROR_INTERNAL.value,
                            f"Execute job failed.")
        return False
    finally:
        resource_common_instance.clear_pwd()
        clear(SysData.SYS_STDIN)
    return ret

if __name__ == '__main__':
    """
    功能描述：主任务执行
    参数：
    @func_type：argv[1] 方法类型
    @pid：argv[2] 请求ID
    例：python3 exchange_resource.py fun_type pid
    """
    log.info("Enter Exchange Resource")
    # 接收由框架写入到stdin中的敏感信息
    for line in sys.stdin:
        SysData.SYS_STDIN = line
        break
    args = sys.argv[1:]
    if len(args) < 2:
        log.error("No enough parameters, param cnt: %s.", len(args))
        sys.exit(1)
    resource_main(args)
    sys.exit(0)